package com.msun.auth.apiAdmin.handler;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.msun.auth.apiAdmin.AdminContext;
import com.msun.auth.custom.annotation.ContextWithToken;
import com.msun.auth.custom.annotation.ResponseResult;
import com.msun.auth.custom.comdto.DataBox;
import com.msun.auth.custom.comdto.PageListDto;
import com.msun.auth.custom.constants.RoleLevels;
import com.msun.auth.custom.exception.BizException;
import com.msun.auth.custom.exception.BizResultCode;
import com.msun.auth.database.model.*;
import com.msun.auth.utils.CheckUtils;
import com.msun.auth.utils.id.IdGenerator;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;

/**
 * 类描述：
 *
 * @ClassName AppManager
 * @Description TODO
 * @Author gj
 * @Date 2023/8/19 12:54
 * @Version 1.0
 */
@Api(tags = "应用管理")
@ResponseResult
@ContextWithToken
@RestController
@RequestMapping("/admin/app/")
public class AppManager {

    @Autowired
    private AppMapper appMapper;
    @Autowired
    private AppUserMapper appUserMapper;
    @Autowired
    private MenuMapper menuMapper;
    @Autowired
    private UserMenuMapper userMenuMapper;
    @Autowired
    private GroupUserMapper groupUserMapper;
    @Autowired
    private GroupRoleMapper groupRoleMapper;

    @Data
    public static class CreateAppArgs {
        private String appName;
        private String serverIp;
        private Integer serverPort;
        private String frontIp;
        private Integer frontPort;
        private String activeRule;
        private Long adminUid;
    }

    @ApiOperation("小组管理员-创建应用")
    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = AppMo.class)})
    @Transactional(rollbackFor = Exception.class)
    @PostMapping("groupAdminCreateApp")
    public AppMo groupAdminCreateApp(@RequestBody CreateAppArgs args) {
        AdminContext.Context ctx = AdminContext.get();
        if (ctx.getRoleLevel() < RoleLevels.GROUP_ADMIN) {
            throw new BizException(BizResultCode.NoRight);
        }
        if (new LambdaQueryChainWrapper<>(appMapper)
                .eq(AppMo::getActiveRule, args.getActiveRule())
                .exists()) {
            throw new BizException(BizResultCode.Error, StrUtil.format("已经存在根路由为【{}】的应用，请更换路由名称", args.getActiveRule()));
        }
        if (new LambdaQueryChainWrapper<>(appMapper)
                .eq(AppMo::getAppName, args.getAppName())
                .exists()) {
            throw new BizException(BizResultCode.Error, StrUtil.format("已经存在应用【{}】，请改用其它名称", args.getAppName()));
        }
        AppMo app = new AppMo();
        app.setId(IdGenerator.ins().generator());
        app.setGroupId(ctx.getGroupId());
        app.setAppName(args.getAppName());
        app.setServerIp(args.getServerIp());
        app.setServerPort(args.getServerPort());
        app.setFrontIp(args.getFrontIp());
        app.setFrontPort(args.getFrontPort());
        app.setActiveRule(args.getActiveRule());
        app.setCreateTime(new Date());
        appMapper.insert(app);
        GroupRoleMo groupRole = new LambdaQueryChainWrapper<>(groupRoleMapper)
                .eq(GroupRoleMo::getGroupId, ctx.getGroupId())
                .eq(GroupRoleMo::getRoleLevel, RoleLevels.APP_ADMIN)
                .one();
        if (groupRole == null) {
            throw new BizException(BizResultCode.Error, "该小组下的应用管理角色已被删除");
        }
        AppUserMo appUser = new AppUserMo();
        appUser.setId(IdGenerator.ins().generator());
        appUser.setUid(args.getAdminUid());
        appUser.setAppId(app.getId());
        appUser.setGroupId(ctx.getGroupId());
        appUser.setGroupRoleId(groupRole.getId());
        appUser.setCreateTime(new Date());
        appUserMapper.insert(appUser);
        return app;
    }

    @Data
    public static class EditAppInfo {
        private Long appId;
        private String appName;
        private String serverIp;
        private Integer serverPort;
        private String frontIp;
        private Integer frontPort;
        private String activeRule;
    }

    @ApiOperation("小组and应用管理员-编辑应用信息")
    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = AppMo.class)})
    @Transactional(rollbackFor = Exception.class)
    @PostMapping("groupAdminEditAppInfo")
    public AppMo groupAdminEditAppInfo(@RequestBody EditAppInfo args) {
        AdminContext.Context ctx = AdminContext.get();
        if (ctx.getRoleLevel() < RoleLevels.APP_ADMIN) {
            throw new BizException(BizResultCode.NoRight);
        }
        if (ctx.getRoleLevel() == RoleLevels.APP_ADMIN) {
            if (args.getAppId() != null && !args.getAppId().equals(ctx.getAppId())) {
                throw new BizException(BizResultCode.NoRight);
            } else {
                args.setAppId(ctx.getAppId());
            }
        }
        if (StrUtil.isNotBlank(args.getActiveRule()) && new LambdaQueryChainWrapper<>(appMapper)
                .ne(AppMo::getId, args.getAppId())
                .eq(AppMo::getActiveRule, args.getActiveRule())
                .exists()) {
            throw new BizException(BizResultCode.Error, StrUtil.format("已经存在根路由为【{}】的应用，请更换路由名称", args.getActiveRule()));
        }
        if (StrUtil.isNotBlank(args.getAppName()) && new LambdaQueryChainWrapper<>(appMapper)
                .ne(AppMo::getId, args.getAppId())
                .eq(AppMo::getAppName, args.getAppName())
                .exists()) {
            throw new BizException(BizResultCode.Error, StrUtil.format("已经存在应用【{}】，请改用其它名称", args.getAppName()));
        }

        AppMo app = new LambdaQueryChainWrapper<>(appMapper)
                .eq(AppMo::getId, args.getAppId())
                .one();
        if (app == null) {
            throw new BizException(BizResultCode.RecordNotExist, "应用不存在");
        }
        if (StrUtil.isNotBlank(args.getAppName())) {
            app.setAppName(args.getAppName());
        }
        if (StrUtil.isNotBlank(args.getServerIp())) {
            app.setServerIp(args.getServerIp());
        }
        if (args.getServerPort() != null && args.getServerPort() > 0) {
            app.setServerPort(args.getServerPort());
        }
        if (StrUtil.isNotBlank(args.getFrontIp())) {
            app.setFrontIp(args.getFrontIp());
        }
        if (args.getFrontPort() != null && args.getFrontPort() > 0) {
            app.setFrontPort(args.getFrontPort());
        }
        if (StrUtil.isNotBlank(args.getActiveRule())) {
            app.setActiveRule(args.getActiveRule());
        }
        appMapper.updateById(app);
        return app;
    }


    @Data
    public static class DelAppArgs {
        private Long appId;
    }

    @ApiOperation("小组管理员-删除应用")
    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = DataBox.class)})
    @Transactional(rollbackFor = Exception.class)
    @PostMapping("groupAdminDelApp")
    public DataBox groupAdminDelApp(@RequestBody DelAppArgs args) {
        AdminContext.Context ctx = AdminContext.get();
        if (ctx.getRoleLevel() < RoleLevels.GROUP_ADMIN) {
            throw new BizException(BizResultCode.NoRight);
        }
        if (new LambdaQueryChainWrapper<>(appUserMapper)
                .eq(AppUserMo::getId, args.getAppId())
                .exists()) {
            throw new BizException(BizResultCode.Error, "请删除该项目下的所有成员");
        }
        QueryWrapper<MenuMo> menuWrapper = new QueryWrapper<>();
        menuWrapper.lambda().eq(MenuMo::getAppId, args.getAppId());
        menuMapper.delete(menuWrapper);
        QueryWrapper<UserMenuMo> userMenuWrapper = new QueryWrapper<>();
        userMenuWrapper.lambda().eq(UserMenuMo::getAppId, args.getAppId());
        userMenuMapper.delete(userMenuWrapper);
        return DataBox.of(true);
    }

    @Data
    public static class EditAppUserArgs {
        private Long uid;
        private Long groupRoleId;
    }

    @ApiOperation("应用管理员-编辑应用用户")
    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = AppUserMo.class)})
    @Transactional(rollbackFor = Exception.class)
    @PostMapping("appAdminEditAppUser")
    public AppUserMo appAdminEditAppUser(@RequestBody EditAppUserArgs args) {
        AdminContext.Context ctx = AdminContext.get();
        if (ctx.getRoleLevel() < RoleLevels.APP_ADMIN) {
            throw new BizException(BizResultCode.NoRight);
        }
        AppUserMo appUser = new LambdaQueryChainWrapper<>(appUserMapper)
                .eq(AppUserMo::getAppId, ctx.getAppId())
                .eq(AppUserMo::getUid, args.getUid())
                .one();
        boolean create = false;
        if (appUser == null) {
            create = true;
            appUser = new AppUserMo();
            appUser.setId(IdGenerator.ins().generator());
        }
        appUser.setUid(args.getUid());
        appUser.setAppId(ctx.getAppId());
        appUser.setGroupId(ctx.getGroupId());
        appUser.setGroupRoleId(args.getGroupRoleId());
        if (create) {
            appUserMapper.insert(appUser);
        } else {
            appUserMapper.updateById(appUser);
        }
        return appUser;
    }

    @Data
    public static class DelAppUserArgs {
        private Long uid;
    }

    @ApiOperation("应用管理员-删除应用用户")
    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = DataBox.class)})
    @Transactional(rollbackFor = Exception.class)
    @PostMapping("appAdminDelAppUser")
    public DataBox appAdminDelAppUser(@RequestBody DelAppUserArgs args) {
        AdminContext.Context ctx = AdminContext.get();
        if (ctx.getRoleLevel() < RoleLevels.APP_ADMIN) {
            throw new BizException(BizResultCode.NoRight);
        }
        appUserMapper.deleteById(args.getUid());
        return DataBox.of(true);
    }

    @Data
    public static class EditMenuArgs {
        private Long id;
        private String title;
        private String keyId;
        private String pathUrl;
        private Long parentId = 0L;
    }

    @ApiOperation("应用管理员-编辑应用菜单")
    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = MenuMo.class)})
    @Transactional(rollbackFor = Exception.class)
    @PostMapping("appAdminEditMenu")
    public MenuMo appAdminEditMenu(@RequestBody EditMenuArgs args) {
        AdminContext.Context ctx = AdminContext.get();
        if (ctx.getRoleLevel() < RoleLevels.APP_ADMIN) {
            throw new BizException(BizResultCode.NoRight);
        }
        MenuMo menu;
        boolean create = false;
        if (args.getId() != null && args.getId() > 0) {
            menu = new LambdaQueryChainWrapper<>(menuMapper)
                    .eq(MenuMo::getId, args.getId())
                    .one();
        } else {
            create = true;
            menu = new MenuMo();
            menu.setId(IdGenerator.ins().generator());
        }
        menu.setAppId(ctx.getAppId());
        menu.setTitle(args.getTitle());
        menu.setKeyId(args.getKeyId());
        menu.setPathUrl(args.getPathUrl());
        menu.setParentId(args.getParentId());
        if (create) {
            menuMapper.insert(menu);
        } else {
            menuMapper.updateById(menu);
        }
        return menu;
    }

    @Data
    public static class DelMenuArgs {
        private Long menuId;
    }

    @ApiOperation("应用管理员-删除应用菜单")
    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = DataBox.class)})
    @Transactional(rollbackFor = Exception.class)
    @PostMapping("appAdminDelMenu")
    public DataBox appAdminDelMenu(@RequestBody DelMenuArgs args) {
        AdminContext.Context ctx = AdminContext.get();
        if (ctx.getRoleLevel() < RoleLevels.APP_ADMIN) {
            throw new BizException(BizResultCode.NoRight);
        }
        QueryWrapper<UserMenuMo> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(UserMenuMo::getMenuId, args.getMenuId());
        userMenuMapper.delete(wrapper);
        menuMapper.deleteById(args.getMenuId());
        return DataBox.of(true);
    }

    @Data
    public static class AuthMenuToUserArgs {
        private Long uid;
        private List<Long> menuIds;
    }

    @ApiOperation("应用管理员-给用户授权菜单")
    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = DataBox.class)})
    @Transactional(rollbackFor = Exception.class)
    @PostMapping("appAdminAuthMenuToUser")
    public DataBox appAdminAuthMenuToUser(@RequestBody AuthMenuToUserArgs args) {
        AdminContext.Context ctx = AdminContext.get();
        if (ctx.getRoleLevel() < RoleLevels.APP_ADMIN) {
            throw new BizException(BizResultCode.NoRight);
        }
        //删除多余的
        QueryWrapper<UserMenuMo> wrapper = new QueryWrapper<>();
        wrapper.lambda()
                .eq(UserMenuMo::getAppId, ctx.getAppId())
                .eq(UserMenuMo::getUid, args.getUid())
                .notIn(UserMenuMo::getMenuId, args.getMenuIds());
        userMenuMapper.delete(wrapper);
        //添加新增的
        List<UserMenuMo> um = new LambdaQueryChainWrapper<>(userMenuMapper)
                .select(UserMenuMo::getMenuId)
                .eq(UserMenuMo::getAppId, ctx.getAppId())
                .eq(UserMenuMo::getUid, args.getUid())
                .list();
        Map<Long, Boolean> umap = new HashMap<>();
        for (UserMenuMo item : um) {
            umap.put(item.getMenuId(), true);
        }
        List<UserMenuMo> userMenus = new ArrayList<>();
        for (Long menuId : args.getMenuIds()) {
            if (umap.getOrDefault(menuId, false)) {
                continue;
            }
            UserMenuMo data = new UserMenuMo();
            data.setAppId(ctx.getAppId());
            data.setUid(args.getUid());
            data.setMenuId(menuId);
            userMenus.add(data);
        }
        if (CollUtil.isNotEmpty(userMenus)) {
            userMenuMapper.insertBatch(userMenus);
        }
        return DataBox.of(true);
    }

    @Data
    public static class DelUserMenuArgs {
        private Long id;
    }

    @ApiOperation("应用管理员-删除用户的菜单")
    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = DataBox.class)})
    @Transactional(rollbackFor = Exception.class)
    @PostMapping("appAdminDelUserMenu")
    public DataBox appAdminDelUserMenu(@RequestBody DelUserMenuArgs args) {
        AdminContext.Context ctx = AdminContext.get();
        if (ctx.getRoleLevel() < RoleLevels.APP_ADMIN) {
            throw new BizException(BizResultCode.NoRight);
        }
        userMenuMapper.deleteById(args.getId());
        return DataBox.of(true);
    }

    @ApiOperation("应用管理员-获取应用菜单列表")
    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = MenuMo.class)})
    @PostMapping("appAdminGetAppMenuList")
    public PageListDto<MenuMo> appAdminGetAppMenuList() {
        AdminContext.Context ctx = AdminContext.get();
        if (ctx.getRoleLevel() < RoleLevels.APP_ADMIN) {
            throw new BizException(BizResultCode.NoRight);
        }
        List<MenuMo> menuMos = recursionGetAppMenuList(ctx.getAppId(), 0L);
        PageListDto<MenuMo> dto = new PageListDto<>();
        dto.setArr(menuMos);
        return dto;
    }

    private List<MenuMo> recursionGetAppMenuList(Long appId, Long parentId) {
        List<MenuMo> menus = new LambdaQueryChainWrapper<>(menuMapper)
                .eq(MenuMo::getAppId, appId)
                .eq(MenuMo::getParentId, parentId)
                .list();
        if (CollUtil.isEmpty(menus)) {
            return Collections.emptyList();
        }
        for (MenuMo menu : menus) {
            menu.setChildren(recursionGetAppMenuList(appId, menu.getId()));
        }
        return menus;
    }

    @Data
    public static class GetUserMenuListArgs {
        private Long uid;
    }

    @ApiOperation("应用管理员-获取用户菜单列表")
    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = MenuMo.class)})
    @PostMapping("appAdminGetUserMenuList")
    public PageListDto<MenuMo> appAdminGetUserMenuList(@RequestBody GetUserMenuListArgs args) {
        AdminContext.Context ctx = AdminContext.get();
        if (ctx.getRoleLevel() < RoleLevels.APP_ADMIN) {
            throw new BizException(BizResultCode.NoRight);
        }
        List<MenuMo> menuMos = recursionGetUserMenuList(ctx.getAppId(), args.getUid(), 0L);
        PageListDto<MenuMo> dto = new PageListDto<>();
        dto.setArr(menuMos);
        return dto;
    }

    private List<MenuMo> recursionGetUserMenuList(Long appId, Long uid, Long parentId) {
        List<MenuMo> menus = menuMapper.adminGetUserMenuList(appId, uid, parentId);
        if (CollUtil.isEmpty(menus)) {
            return Collections.emptyList();
        }
        for (MenuMo menu : menus) {
            menu.setChildren(recursionGetUserMenuList(appId, uid, menu.getId()));
        }
        return menus;
    }

    @Data
    public static class GetAppListArgs {
        private Integer pageNo;
        private Integer pageSize;
        private String keyword;
    }

    @ApiOperation("小组管理员-获取应用列表")
    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = MenuMo.class)})
    @PostMapping("groupAdminGetAppList")
    public PageListDto<AppMo> groupAdminGetAppList(@RequestBody GetAppListArgs args) {
        AdminContext.Context ctx = AdminContext.get();
        if (ctx.getRoleLevel() < RoleLevels.GROUP_ADMIN) {
            throw new BizException(BizResultCode.NoRight);
        }
        if (CheckUtils.isZero(args.getPageNo())) {
            args.setPageNo(1);
        }
        if (CheckUtils.isZero(args.getPageSize())) {
            args.setPageSize(20);
        }
        Page<AppMo> page = new LambdaQueryChainWrapper<>(appMapper)
                .eq(AppMo::getGroupId, ctx.getGroupId())
                .like(StrUtil.isNotBlank(args.getKeyword()), AppMo::getAppName, args.getKeyword())
                .orderByAsc(AppMo::getId)
                .page(new Page<>(args.getPageNo(), args.getPageSize(), true));
        PageListDto<AppMo> dto = new PageListDto<>();
        dto.setArr(page.getRecords());
        dto.setPageNo(args.getPageNo());
        dto.setPageSize(args.getPageSize());
        dto.setTotal(page.getTotal());
        return dto;
    }

    @Data
    public static class GetAppUserListArgs {
        private Integer pageNo;
        private Integer pageSize;
        private String keyword;
    }
    @ApiOperation("应用管理-员获取成员列表")
    @ApiResponses({@ApiResponse(code = 200, message = "成功", response = MenuMo.class)})
    @PostMapping("appAdminGetAppUserList")
    public PageListDto<UserMo> appAdminGetAppUserList(@RequestBody GetAppUserListArgs args) {
        AdminContext.Context ctx = AdminContext.get();
        if (ctx.getRoleLevel() < RoleLevels.APP_ADMIN) {
            throw new BizException(BizResultCode.NoRight);
        }
        if (CheckUtils.isZero(args.getPageNo())) {
            args.setPageNo(1);
        }
        if (CheckUtils.isZero(args.getPageSize())) {
            args.setPageSize(20);
        }
        String keyword = null;
        if (StrUtil.isNotBlank(args.getKeyword())) {
            keyword = StrUtil.format("%{}%", args.getKeyword());
        }
        List<UserMo> list = appUserMapper.getUserList(ctx.getAppId(), (args.getPageNo() - 1) * args.getPageSize(), args.getPageSize(), keyword);
        Integer count = appUserMapper.getUserCount(ctx.getAppId(), keyword);
        PageListDto<UserMo> dto = new PageListDto<>();
        dto.setArr(list);
        dto.setPageNo(args.getPageNo());
        dto.setPageSize(args.getPageSize());
        dto.setTotal(count);
        return dto;
    }


}